/*
    Dominate 3D - A cube-based game of life

    Copyright (C) 2007, Craig H. Miller (kidmosey@gmail.com)

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __CVector3DD_H__
#define __CVector3DD_H__

#define _USE_MATH_DEFINES
#include <math.h>

/** A standard 3D point
 *  Actually, this can be used for any 3 dimensional floating
 *  point representation.
 **/
class CVector3D
{
public:
	/** constructor
	 *	\param _x X coordinate
	 *	\param _y Y coordinate
	 *	\param _z Z coordinate
	 */
	CVector3D(float _x = 0.0f, float _y = 0.0f, float _z = 0.0f);

	/** constructor
	 *	\param strIn input string
	 *
	 *	Takes a string in the form "#.# #.# #.#", where #.# is a floating
	 *	point number.  Each number is assigned to x, y, and z, respectively.
	 *
	 */
	CVector3D(const char *strIn);

	/// destructor
	~CVector3D();

	/// equality operator
	bool operator== (const CVector3D &rhs) const;

	/// add a vector to this vector and return the result
	CVector3D  operator+  (const CVector3D &rhs) const;

	/// add a vector to this vector
	CVector3D &operator+= (const CVector3D &rhs);

	/// subtract a vector from this vector and return the result
	CVector3D operator- (const CVector3D &rhs) const;

	/// negate this vector and return the result
	CVector3D operator- (void) const;

	/// multiply this vector by a float and return the result
	CVector3D  operator*  (float rhs) const;

	/// multiply this vector by a float
	CVector3D &operator*= (float rhs);

	/// array accessor
	float &operator[](int i)
	{
		return(((float*)this)[i]);
	}

	/// translate this angle vector from degrees to radians
	CVector3D toRadians() const
	{
		float r = (float )M_PI/180.0f;
		return CVector3D(x*r, y*r, z*r);
	}

    /// multiply two vectors together
	CVector3D operator* (const CVector3D &rhs) const
	{
		return CVector3D(
                    x * rhs.x,
                    y * rhs.y,
                    z * rhs.z
        );
	}

	/// return the dot product of this and another vector.
	float dot (const CVector3D &rhs) const
	{
		return x * rhs.x +
			   y * rhs.y +
			   z * rhs.z;
	}

	/// return the cross product of this and another vector.
	CVector3D cross(const CVector3D &rhs)
	{
		return CVector3D(
			y*rhs.z - z*rhs.y,
			z*rhs.x - x*rhs.z,
			x*rhs.y - y*rhs.x
		);
	}

	/// normalize this vector
	void normalize()
	{
		float s = 1.0f / sqrtf(x*x+y*y+z*z);
		x *= s;
		y *= s;
		z *= s;
	}

    /// return a normalized version of this vector
    CVector3D normal() const
    {
		float s = 1.0f / sqrtf(x*x+y*y+z*z);
        return CVector3D(
            x * s,
            y * s,
            z * s
        );
    }

	/// return the distance of this vector from the origin
	float length() const									{return sqrtf(x*x+y*y+z*z);}

	/// return the squared distance of this vector from the origin
	float length2() const									{return x*x+y*y+z*z;}

	/// return the squared distance between two vectors
	float dist2(const CVector3D &a) const					{return (x-a.x)*(x-a.x)+(y-a.y)*(y-a.y)+(z-a.z)*(z-a.z);}

	/** linearly interpolate this vector.
	 * \param t Time [0.0, 1.0]
	 * \param a Start vector
	 * \param b End vector
	 */
	void lerp(float t, const CVector3D &a, const CVector3D &b)
	{
		*this = a + ((b-a) * t);
	}

	float x, y, z;
};

/** convert an angle vector to radians
 * \param v Degree vector to convert
 *
 */
inline CVector3D ToRadians(const CVector3D &v)
{
    float m = (float )M_PI * (1.0f / 180.0f);
    return CVector3D(
        v.x * m,
        v.y * m,
        v.z * m
    );
}

#endif // __CVector3DD_H__
